#include "FBOManager.h"
#include <glee.h>

FBOManager FBOManager::m_Instance;

FBOManager::FBOManager() : m_FBO(0), m_MaxBuffers(0), m_SceneImg(0), 
						   m_depthbuff(0), m_screenHeight(1), m_screenWidth(1)
{

}

FBOManager::~FBOManager()
{

}

bool FBOManager::Init(int _screenWidth, int _screenHeight)
{
	m_screenWidth = abs(_screenWidth);
	m_screenHeight = abs(_screenHeight);

	if(m_screenWidth == 0) m_screenWidth = 1;
	if(m_screenHeight == 0) m_screenHeight = 1;

	//////////////////////////////////////////////////////////////////////////
	//FBO Stuff
	/////////////////////////////////////////////////////////////////////////
	glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &m_MaxBuffers);

	glGenFramebuffersEXT(1, &m_FBO);
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FBO);

	//////////////////////////////////////////////////////////////////////////
	// Create the render buffer
	//////////////////////////////////////////////////////////////////////////
	glGenRenderbuffersEXT(1, &m_depthbuff);
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthbuff);
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, _screenWidth, _screenHeight);

	glGenRenderbuffersEXT(1, &m_stencilBuffer);
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_stencilBuffer);
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8, _screenWidth, _screenHeight);

	//////////////////////////////////////////////////////////////////////////
	//Yay Textures
	//////////////////////////////////////////////////////////////////////////
	glGenTextures(1, &m_SceneImg);
	glBindTexture(GL_TEXTURE_2D, m_SceneImg);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _screenWidth, _screenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	//////////////////////////////////////////////////////////////////////////
	// And attach it to the FBO so we can render to it
	//////////////////////////////////////////////////////////////////////////
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_SceneImg, 0);

	//////////////////////////////////////////////////////////////////////////
	// Attach the depth render buffer to the FBO as it's depth attachment
	//////////////////////////////////////////////////////////////////////////
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthbuff);
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_stencilBuffer);

	// Inform openGL that we wish to render to this target
	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);

	//////////////////////////////////////////////////////////////////////////
	//Check to see if the FBO is up and running
	//////////////////////////////////////////////////////////////////////////
	int fboStatus = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
	if ( fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT )
	{
		FBOERR("Framebuffer Initialization Failed", "FBO Manager", false);
	}

	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
	glBindTexture(GL_TEXTURE_2D, -1);

	//////////////////////////////////////////////////////////////////////////
	//Yay end FBO Creation
	//////////////////////////////////////////////////////////////////////////

	return true;
}

void FBOManager::Shutdown(void)
{
	glDeleteFramebuffersEXT(1, &m_FBO);
	glDeleteRenderbuffersEXT(1, &m_depthbuff);
	glDeleteRenderbuffersEXT(1, &m_stencilBuffer);
	glDeleteTextures(1, &m_SceneImg);
}

bool FBOManager::PushFBO(void)
{
	// Save draw buffer
	glGetIntegerv(GL_DRAW_BUFFER, &m_drawBuffer);

	// Bind fbo now
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FBO);

	// Inform openGL that we wish to render to this target
	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);

	// Save viewport
	glPushAttrib(GL_VIEWPORT | GL_COLOR_BUFFER_BIT);

	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

	return true;
}

bool FBOManager::PopFBO(void)
{
	glPopAttrib();
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

	glDrawBuffer(m_drawBuffer);

	return true;
}

void FBOManager::DrawScreenAlignedQuad()
{
	glBindTexture(GL_TEXTURE_2D, m_SceneImg);

	glBegin(GL_QUADS);
	{
		glTexCoord2f(0.0f, 1.0f);
		glVertex2f(0.0f, (float)m_screenHeight);
		glTexCoord2f(1.0f, 1.0f); 
		glVertex2f((float)m_screenWidth,(float) m_screenHeight);
		glTexCoord2f(1.0f, 0.0f); 
		glVertex2f((float)m_screenWidth, 0.0f);
		glTexCoord2f(0.0f, 0.0f); 
		glVertex2f(0.0f, 0.0f);
	}
	glEnd();
}

void FBOManager::Resize(int _screenWidth, int _screenHeight)
{
	// Setup new sizes
	m_screenWidth = abs(_screenWidth);
	m_screenHeight = abs(_screenHeight);

	if(m_screenWidth == 0) m_screenWidth = 1;
	if(m_screenHeight == 0) m_screenHeight = 1;

	// Recreate texture and rebind the FBO storage container
	glBindTexture(GL_TEXTURE_2D, m_SceneImg);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_screenWidth, m_screenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT32, m_screenWidth, m_screenHeight);
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8_EXT, m_screenWidth, m_screenHeight);
}